package de.maedn.server;

import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.simple.JSONValue;
import com.google.appengine.api.channel.ChannelMessage;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;

import de.maedn.game.Lobby;
import de.maedn.game.Party;

public class JsonServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		resp.setContentType("application/json");
		resp.setCharacterEncoding("utf-8");
		resp.setHeader("Cache-Control", "no-cache, must-revalidate");
		resp.setHeader("Access-Control-Allow-Origin", "*");
        Principal principal = req.getUserPrincipal();

		String method = req.getParameter("method");
		@SuppressWarnings("unchecked")
		Map<String, Object> data = (Map<String, Object>) JSONValue.parse(req.getParameter("data"));

        Map<String, Object> result = null;
		if (method.equals("login")) {
			result = this.login(principal);
		} else if (method.equals("requestToken")) {
			result = this.requestToken(principal);
		} else if (method.equals("chat")) {
			result = this.chat(data, principal);
		} else if (method.equals("showAllOpenParties")) {
			result = this.openParties();
		} else if (method.equals("createParty")) {
			result = this.createParty(data, principal);
		} else if (method.equals("setPlayer")) {
			result = this.setPlayer(data, principal);
		} else if (method.equals("joinParty")) {
			result = this.joinParty(data, principal);
		} else if (method.equals("leaveParty")) {
			result = this.leaveParty(principal);
		} else if (method.equals("startParty")) {
			result = this.startParty(data, principal);
		} else if (method.equals("moveFigure")) {
			result = this.moveFigure(data, principal);
		} else if (method.equals("rollDice")) {
			result = this.rollDice(data, principal);
		}
		System.out.println(JSONValue.toJSONString(result));
		JSONValue.writeJSONString(result, resp.getWriter());
	}
	
//								startParty														Incl. PUSH
	private Map<String, Object> startParty(Map<String, Object> data, Principal principal) {
		Map<String, Object> map = new HashMap<String, Object>();
		
		EntityManager em = EMF.createEntityManager();
		String select;
		Query query;
		
		select = "SELECT FROM User u WHERE u.account=:account";
		query = em.createQuery(select).setParameter("account", principal.getName());
		User user = (User)query.getSingleResult();
		
		System.out.println(user);
		
		select = "SELECT FROM Party p WHERE p.key=:id";
		query = em.createQuery(select).setParameter("id", user.getInGame());
		Party party = (Party)query.getSingleResult();
		
		party.startGame();
		party.rollDice();
		em.merge(party);
		em.refresh(party);
		em.close();
		
		map.put("status", 0);
		this.gameStartPush(party);
		
		return map;
	}
	
//								moveFigure														Incl. PUSH
	private Map<String, Object> moveFigure(Map<String, Object> data, Principal principal) {
		Map<String, Object> map = new HashMap<String, Object>();
		
		EntityManager em = EMF.createEntityManager();
		String select;
		Query query;
		
		select = "SELECT FROM User u WHERE u.account=:account";
		query = em.createQuery(select).setParameter("account", principal.getName());
		User user = (User)query.getSingleResult();
		
		select = "SELECT FROM Party p WHERE p.key=:id";
		query = em.createQuery(select).setParameter("id", user.getInGame());
		Party party = (Party)query.getSingleResult();
		
		if (party.isSetPlayer1() && party.getPlayer1() == user.getKey().getId()) {
			if (party.getCurrentPlayer() == 0) {
				if (!party.getRollDice()) {
					if (party.move((Integer)data.get("figureID"), 0)) {
						this.gameInfoPush(party);
						map.put("status", 0);
					} else {
						map.put("status", 3);
					}
				} else {
					map.put("status", 6);
				}
			} else {
				map.put("status", 2);
			}
		} else if (party.isSetPlayer2() && party.getPlayer2() == user.getKey().getId()) {
			if (party.getCurrentPlayer() == 0) {
				if (!party.getRollDice()) {
					if (party.move((Integer)data.get("figureID"), 1)) {
						this.gameInfoPush(party);
						map.put("status", 0);
					} else {
						map.put("status", 3);
					}
				} else {
					map.put("status", 6);
				}
			} else {
				map.put("status", 2);
			}
		} else if (party.isSetPlayer3() && party.getPlayer3() == user.getKey().getId()) {
			if (party.getCurrentPlayer() == 0) {
				if (!party.getRollDice()) {
					if (party.move((Integer)data.get("figureID"), 2)) {
						this.gameInfoPush(party);
						map.put("status", 0);
					} else {
						map.put("status", 3);
					}
				} else {
					map.put("status", 6);
				}
			} else {
				map.put("status", 2);
			}
		} else if (party.isSetPlayer3() && party.getPlayer3() == user.getKey().getId()) {
			if (party.getCurrentPlayer() == 0) {
				if (!party.getRollDice()) {
					if (party.move((Integer)data.get("figureID"), 3)) {
						this.gameInfoPush(party);
						map.put("status", 0);
					} else {
						map.put("status", 3);
					}
				} else {
					map.put("status", 6);
				}
			} else {
				map.put("status", 2);
			}
		} else {
			map.put("status", 1);
		}
		em.merge(party);
		em.close();
		return map;
	}
	
//								rollDice														Incl. PUSH
	private Map<String, Object> rollDice(Map<String, Object> data, Principal principal) {
		Map<String, Object> map = new HashMap<String, Object>();
		
		EntityManager em = EMF.createEntityManager();
		String select;
		Query query;
		
		select = "SELECT FROM User u WHERE u.account=:account";
		query = em.createQuery(select).setParameter("account", principal.getName());
		User user = (User)query.getSingleResult();
		
		select = "SELECT FROM Party p WHERE p.key=:id";
		query = em.createQuery(select).setParameter("id", user.getInGame());
		Party party = (Party)query.getSingleResult();
		
		if (party.isSetPlayer1() && party.getPlayer1() == user.getKey().getId()) {
			if (party.getCurrentPlayer() == 0) {
				if (party.getRollDice()) {
					party.rollDice();
					this.gameInfoPush(party);
					map.put("status", 0);
				} else {
					map.put("status", 3);
				}
			} else {
				map.put("status", 2);
			}
		} else if (party.isSetPlayer2() && party.getPlayer2() == user.getKey().getId()) {
			if (party.getCurrentPlayer() == 1) {
				if (party.getRollDice()) {
					party.rollDice();
					this.gameInfoPush(party);
					map.put("status", 0);
				} else {
					map.put("status", 3);
				}
			} else {
				map.put("status", 2);
			}
		} else if (party.isSetPlayer3() && party.getPlayer3() == user.getKey().getId()) {
			if (party.getCurrentPlayer() == 2) {
				if (party.getRollDice()) {
					party.rollDice();
					this.gameInfoPush(party);
					map.put("status", 0);
				} else {
					map.put("status", 3);
				}
			} else {
				map.put("status", 2);
			}
		} else if (party.isSetPlayer3() && party.getPlayer3() == user.getKey().getId()) {
			if (party.getCurrentPlayer() == 3) {
				if (party.getRollDice()) {
					party.rollDice();
					this.gameInfoPush(party);
					map.put("status", 0);
				} else {
					map.put("status", 3);
				}
			} else {
				map.put("status", 2);
			}
		} else {
			map.put("status", 1);
		}
		em.merge(party);
		em.close();
		return map;
	}
	
//								leaveParty														Incl. PUSH
	private Map<String, Object> leaveParty(Principal principal) {
		Map<String, Object> map = new HashMap<String, Object>();
		
		EntityManager em = EMF.createEntityManager();
		String select;
		Query query;
		
		select = "SELECT FROM User u WHERE u.account=:account";
		query = em.createQuery(select).setParameter("account", principal.getName());
		User user = (User)query.getSingleResult();
		
		select = "SELECT FROM Party p WHERE p.key=:id";
		query = em.createQuery(select).setParameter("id", user.getInGame());
		Party party = (Party)query.getSingleResult();
		
		if (!party.getStarted()) {
			if (party.getHostPlayer() == user.getKey().getId()) {
				ArrayList<User> liste = new ArrayList<User>();
				if (party.isSetPlayer1()) {
					select = "SELECT FROM User u WHERE u.key=:id";
					query = em.createQuery(select).setParameter("id", party.getPlayer1());
					liste.add((User)query.getSingleResult());
					liste.get(liste.size() - 1).setInGame(new Long(-1));
				}
				if (party.isSetPlayer2()) {
					select = "SELECT FROM User u WHERE u.key=:id";
					query = em.createQuery(select).setParameter("id", party.getPlayer2());
					liste.add((User)query.getSingleResult());
					liste.get(liste.size() - 1).setInGame(new Long(-1));
				}
				if (party.isSetPlayer3()) {
					select = "SELECT FROM User u WHERE u.key=:id";
					query = em.createQuery(select).setParameter("id", party.getPlayer3());
					liste.add((User)query.getSingleResult());
					liste.get(liste.size() - 1).setInGame(new Long(-1));
				}
				if (party.isSetPlayer4()) {
					select = "SELECT FROM User u WHERE u.key=:id";
					query = em.createQuery(select).setParameter("id", party.getPlayer4());
					liste.add((User)query.getSingleResult());
					liste.get(liste.size() - 1).setInGame(new Long(-1));
				}
				em.remove(party);
				em.merge(user);
				em.close();
				try {
					Thread.currentThread();
					Thread.sleep(500);
				} catch (InterruptedException ie) {
					
				}
		    	this.partyLobbyPush(liste);
			} else {
				if (party.isSetPlayer1() && party.getPlayer1() == user.getKey().getId()) {
					party.setPlayer1(null);
				}
				if (party.isSetPlayer2() && party.getPlayer2() == user.getKey().getId()) {
					party.setPlayer2(null);
				}
				if (party.isSetPlayer3() && party.getPlayer3() == user.getKey().getId()) {
					party.setPlayer3(null);
				}
				if (party.isSetPlayer4() && party.getPlayer4() == user.getKey().getId()) {
					party.setPlayer4(null);
				}
				user.setInGame(new Long(-1));
				em.close();
				try {
					Thread.currentThread();
					Thread.sleep(500);
				} catch (InterruptedException ie) {
					
				}
		    	this.partyLobbyPush(party);
			}
			this.mainLobbyPush();
			map.put("status", 0);
			return map;
		} else {
			map.put("status", 3);
			return map;
		}
	}

//								joinParty														Incl. PUSH
	private Map<String, Object> joinParty(Map<String, Object> data, Principal principal) {
		Map<String, Object> map = new HashMap<String, Object>();
		EntityManager em = EMF.createEntityManager();
		String select;
		Query query;
		
		select = "SELECT FROM User u WHERE u.account=:account";
		query = em.createQuery(select).setParameter("account", principal.getName());
		User user = (User)query.getSingleResult();
		
//		System.out.println(user);
		
		select = "SELECT FROM Party p WHERE p.key=:id";
		query = em.createQuery(select).setParameter("id", (Long)data.get("partyID"));
		Party party = (Party)query.getSingleResult();
		
//		System.out.println(party);
		
		if (party.getPlayerCount() == 4) {
			map.put("status", 5);
		} else if (party.getStarted()) {
			map.put("status", 3);
		} else {
			Boolean xyz = true;
			if (party.isSetPlayer1()) {
				if (party.getPlayer1().equals(user.getKey().getId())) {
					xyz = false;
				}
			}
			if (party.isSetPlayer2()) {
				if (party.getPlayer2().equals(user.getKey().getId())) {
					xyz = false;
				}
			}
			if (party.isSetPlayer3()) {
				if (party.getPlayer3().equals(user.getKey().getId())) {
					xyz = false;
				}
			}
			if (party.isSetPlayer4()) {
				if (party.getPlayer4().equals(user.getKey().getId())) {
					xyz = false;
				}
			}
			if (!xyz) {
				map.put("status", 2);
				user.setInGame((Long)data.get("partyID"));
			} else {
				map.put("status", 0);
				user.setInGame((Long)data.get("partyID"));
//				System.out.println(user);
				if (!party.isSetPlayer1()) {
					party.setPlayer1(user.getKey().getId());
					map.put("Spielernummer", 1);
				} else if (!party.isSetPlayer2()) {
					party.setPlayer2(user.getKey().getId());
					map.put("Spielernummer", 2);
				} else if (!party.isSetPlayer3()) {
					party.setPlayer3(user.getKey().getId());
					map.put("Spielernummer", 3);
				} else if (!party.isSetPlayer4()) {
					party.setPlayer4(user.getKey().getId());
					map.put("Spielernummer", 4);
				}
//				System.out.println(party);
			}
		}
		
		try {
			Thread.currentThread();
			Thread.sleep(500);
		} catch (InterruptedException ie) {
			
		}
    	this.mainLobbyPush();
    	this.partyLobbyPush(party);
		
    	em.merge(party);
    	em.merge(user);
    	
		em.close();
		return map;
	}
	
//				 mainLobbyPush
	private void mainLobbyPush() {
		EntityManager em = EMF.createEntityManager();
		String select = "SELECT FROM User u WHERE u.inGame=:game";
		Query query = em.createQuery(select).setParameter("game", -1);
		ArrayList<User> users = new ArrayList<User>();
		try {
			@SuppressWarnings("rawtypes")
			List userListe = query.getResultList();
			System.out.println("Anzahl User: " + userListe.size());
			for (int i = 0; i < userListe.size(); i++) {
				users.add((User)userListe.get(i));
			}
		} catch (NoResultException e) {
			;
		}
		
		Map<String, Object> json = new HashMap<String, Object>();
		json.put("method", "mainLobbyInfo");
		Lobby lobby = new Lobby();
		json.put("data", lobby.Show());
		String message = JSONValue.toJSONString(json);
		for (int i = 0; i < users.size(); i++) {
			ChannelServiceFactory.getChannelService().sendMessage(new ChannelMessage(users.get(i).getAccount(), message));
		}
	}
	
//				 partyLobbyPush
	private void partyLobbyPush(Party p) {
		try {
			EntityManager em = EMF.createEntityManager();
			String select;
			Query query;
			
			Map<String, Object> json = new HashMap<String, Object>();
			Map<String, Object> data = new HashMap<String, Object>();
			ArrayList<String> names = new ArrayList<String>();
			ArrayList<String> accounts = new ArrayList<String>();
			
			json.put("method", "partyLobbyInfo");
			data.put("status", 0);
			
			if (p.isSetPlayer1()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer1());
				User u = (User)query.getSingleResult();
				names.add(u.getName());
				accounts.add(u.getAccount());
			} else {
				names.add(null);
			}
			if (p.isSetPlayer2()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer2());
				User u = (User)query.getSingleResult();
				names.add(u.getName());
				accounts.add(u.getAccount());
			} else {
				names.add(null);
			}
			if (p.isSetPlayer3()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer3());
				User u = (User)query.getSingleResult();
				names.add(u.getName());
				accounts.add(u.getAccount());
			} else {
				names.add(null);
			}
			if (p.isSetPlayer4()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer4());
				User u = (User)query.getSingleResult();
				names.add(u.getName());
				accounts.add(u.getAccount());
			} else {
				names.add(null);
			}
			data.put("playerName", names);
			json.put("data", data);
			String message = JSONValue.toJSONString(json);
			for (int i = 0; i < accounts.size(); i++) {
				ChannelServiceFactory.getChannelService().sendMessage(new ChannelMessage(accounts.get(i), message));
			}
		} catch (NoResultException e) {
			;
		}
	}

//	 			 gameInfoPush
	private void gameInfoPush(Party p) {
		EntityManager em = EMF.createEntityManager();
		String select;
		Query query;
		try {
			Map<String, Object> json = new HashMap<String, Object>();
			Map<String, Object> data = new HashMap<String, Object>();
			ArrayList<String> accounts = new ArrayList<String>();
			
			json.put("method", "gameInfo");
			
			data.put("movedPlayer", p.getMovedPlayer());
			data.put("movedFigure", p.getMovedFigure());
			data.put("oldPos", p.getOldPos());
			data.put("newPos", p.getNewPos());
			data.put("curPlayer", p.getCurrentPlayer());
			data.put("dice", p.getDice());
			
			Integer[][] positions = p.getPositions();
			ArrayList<ArrayList<Integer>> pos = new ArrayList<ArrayList<Integer>>();
			for (int i = 0; i < positions.length; i++) {
				ArrayList<Integer> ppp = new ArrayList<Integer>();
				for (int y = 0; y < positions[i].length; y++) {
					ppp.add(positions[i][y]);
				}
				pos.add(ppp);
			}
			data.put("positions", pos);
			
			if (p.isSetPlayer1()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer1());
				User u = (User)query.getSingleResult();
				accounts.add(u.getAccount());
			} else {
			}
			if (p.isSetPlayer2()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer2());
				User u = (User)query.getSingleResult();
				accounts.add(u.getAccount());
			} else {
			}
			if (p.isSetPlayer3()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer3());
				User u = (User)query.getSingleResult();
				accounts.add(u.getAccount());
			} else {
			}
			if (p.isSetPlayer4()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer4());
				User u = (User)query.getSingleResult();
				accounts.add(u.getAccount());
			} else {
			}
			json.put("data", data);
			String message = JSONValue.toJSONString(json);
			for (int i = 0; i < accounts.size(); i++) {
				ChannelServiceFactory.getChannelService().sendMessage(new ChannelMessage(accounts.get(i), message));
			}
		} catch (NoResultException e) {
			;
		}
	}	

//	 			 gameStartPush
	private void gameStartPush(Party p) {
		EntityManager em = EMF.createEntityManager();
		String select;
		Query query;
		try {
			Map<String, Object> json = new HashMap<String, Object>();
			Map<String, Object> data = new HashMap<String, Object>();
			ArrayList<String> accounts = new ArrayList<String>();
			
			json.put("method", "gameStart");
			
			data.put("curPlayer", p.getCurrentPlayer());
			data.put("dice", p.getDice());
			
			if (p.isSetPlayer1()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer1());
				User u = (User)query.getSingleResult();
				accounts.add(u.getAccount());
			} else {
			}
			if (p.isSetPlayer2()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer2());
				User u = (User)query.getSingleResult();
				accounts.add(u.getAccount());
			} else {
			}
			if (p.isSetPlayer3()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer3());
				User u = (User)query.getSingleResult();
				accounts.add(u.getAccount());
			} else {
			}
			if (p.isSetPlayer4()) {
				select = "SELECT FROM User u WHERE u.key=:id";
				query = em.createQuery(select).setParameter("id", p.getPlayer4());
				User u = (User)query.getSingleResult();
				accounts.add(u.getAccount());
			} else {
			}
			json.put("data", data);
			String message = JSONValue.toJSONString(json);
			for (int i = 0; i < accounts.size(); i++) {
				ChannelServiceFactory.getChannelService().sendMessage(new ChannelMessage(accounts.get(i), message));
			}
		} catch (NoResultException e) {
			;
		}
	}
	
//	 			 partyLobbyPush	
	private void partyLobbyPush(ArrayList<User> users) {
		Map<String, Object> json = new HashMap<String, Object>();
		json.put("method", "partyLobbyInfo");
		Map<String, Object> data = new HashMap<String, Object>();
		data.put("status", 1);
		data.put("playerName", null);
		json.put("data", data);
		String message = JSONValue.toJSONString(json);
		for (int i = 0; i < users.size(); i++) {
			ChannelServiceFactory.getChannelService().sendMessage(new ChannelMessage(users.get(i).getAccount(), message));
		}
	}
	
//								setPlayer														Incl. PUSH
 	private Map<String, Object> setPlayer(Map<String, Object> data, Principal principal) {
		String account = principal.getName();

        EntityManager em = EMF.createEntityManager();
		String select = "SELECT FROM User u WHERE u.account=:account";
		Query query = em.createQuery(select).setParameter("account", account);
		User user = (User)query.getSingleResult();
		user.setName((String)data.get("nickname"));
		em.merge(user);
		em.close();
		
		Map<String, Object> map = new HashMap<String, Object>();
    	map.put("status", 0);
    	this.mainLobbyPush();
		return map;
	}
	
//								createParty														Incl. PUSH
	private Map<String, Object> createParty(Map<String, Object> data, Principal principal) {
		Lobby neu = new Lobby();
		
		String account = principal.getName();

        EntityManager em = EMF.createEntityManager();
		String select = "SELECT FROM User u WHERE u.account=:account";
		Query query = em.createQuery(select).setParameter("account", account);
		User user = (User)query.getSingleResult();
		Party p = neu.createParty((String)data.get("partyName"), user.getKey().getId(), 1);
    	em.persist(p);
    	em.merge(p);
    	em.refresh(p);
		
    	System.out.println(p);
    	
		user.setInGame(p.getKey().getId());
		em.merge(user);
		em.close();
		
		System.out.println(user);
		
    	this.mainLobbyPush();
    	
    	Map<String, Object> map = new HashMap<String, Object>();
    	map.put("status", 0);
		return map;
	}

//								chat
	private Map<String, Object> chat(Map<String, Object> data, Principal principal) {
		Map<String, Object> json = new HashMap<String, Object>();
		json.put("method", "chat");
		Map<String, Object> data2 = new HashMap<String, Object>();
		data2.put("player", 1);
		data2.put("message", (String)data.get("message"));
		json.put("data", data2);
		String message = JSONValue.toJSONString(json);
		ChannelServiceFactory.getChannelService().sendMessage(new ChannelMessage(principal.getName(), message));
		return null;
	}

//								requestToken
	private Map<String, Object> requestToken(Principal principal) {
		Map<String, Object> result = new HashMap<String, Object>();
		ChannelService channelService = ChannelServiceFactory.getChannelService();
		String token = channelService.createChannel(principal.getName());
		if (token == null || token.equals("")) {
			result.put("status", 1);
		} else {
			result.put("status", 0);
			result.put("token", token);
		}
		return result;
	}
	
//								login
	private Map<String, Object> login(Principal principal) {
		Map<String, Object> result = new HashMap<String, Object>();
		if (principal == null) {
			result.put("status", -1);
		} else {
			String userID = principal.getName(); 
			result.put("status", 0);
			result.put("nickName", userID);
		}
		return result;
	}
	
//								openParties
	private Map<String, Object> openParties() {
		Lobby lobby = new Lobby();
		return lobby.Show();
	}
	
//				doPost
	public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
		doGet(req, resp);
	}

}